package com.example.oauth.tracing.config;

import com.example.oauth.tracing.OwlTraceLog;
import com.example.oauth.tracing.annotation.OwlTraceTag;
import com.example.oauth.utils.JsonUtil;
import com.example.oauth.utils.SpELUtil;
import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.Tracer;
import io.opentracing.tag.Tags;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

@Slf4j
@ConditionalOnProperty(value = "opentracing.jaeger.enabled", havingValue = "true", matchIfMissing = true)
@Configuration
public class OwlTraceAutoConfiguration {

    static final String TAG_COMPONENT = "java";

    @Autowired(required = false)
    @Lazy
    private Tracer tracer;

    @Bean
    public TracingAspect openTracingAspect() {
        return new TracingAspect();
    }

    @Aspect
    class TracingAspect {
        @Around("@annotation(com.example.oauth.tracing.annotation.OwlTrace)")
        public Object traceProcess(ProceedingJoinPoint pjp) throws Throwable {
            if (tracer != null) {
                Span span = null;
                final String cls = pjp.getTarget().getClass().getName();
                final String mName = pjp.getSignature().getName();
                span = tracer.buildSpan(cls + "." + mName)
                        .withTag(Tags.COMPONENT.getKey(), TAG_COMPONENT)
                        .withTag("class", cls)
                        .withTag("method", mName)
                        .start();

                // 记录方法参数
                logInfo(span, pjp.getArgs());
                Object result = null;
                try ( final Scope scope = tracer.scopeManager().activate(span) ) {
                    result = pjp.proceed();
                    return result;
                } catch (Throwable t) {
                    OwlTraceLog.error(span, t);
                    throw t;
                } finally {
                    // 记录放款结果
                    OwlTraceLog.log(span, "result", JsonUtil.toJson(result));

                    if (tracer != null && span != null) {
                        span.finish();
                    }
                }
            } else {
                return pjp.proceed();
            }
        }

        @Around("@annotation(owlTraceTag)")
        public Object traceTagProcess(ProceedingJoinPoint pjp, OwlTraceTag owlTraceTag) throws Throwable {
            try {
                if (tracer != null && !StringUtils.isEmpty(owlTraceTag.key()) && !StringUtils.isEmpty(owlTraceTag.value())) {
                    Span span = tracer.activeSpan();
                    if (span != null) {
                        OwlTraceLog.setTag(span, owlTraceTag.key(), SpELUtil.getScriptValue(pjp, owlTraceTag.value()));
                    }
                }
            } catch (Exception e) {
                log.error("",e);
            }
            return pjp.proceed();
        }
    }

    private void logInfo(Span span, Object[] args) {
        try {
            if (args != null && args.length > 0) {
                for (int i = 0; i < args.length; i++) {
                    //基本类型处理
                    OwlTraceLog.log(span, String.format("arg[%s]", i), JsonUtil.toJson(args[i]));
                }
            }
        } catch (Exception e) {
            log.error("",e);
        }
    }

}
